/**********************************************************************************
// Asteroids Race 
//
// Creation:	07 Out 2009
// Updated:		18 Apr 2011
// Compiler:	Visual Studio 2010
//
// Notes:		Level 1
//
**********************************************************************************/

#include "Asteroids.h"

/**********************************************************************************/


void Asteroids::Level1Init()
{
	// ----------------------------
	// PreLevel 1 Initialization
	// ----------------------------

	// create tutorial like ballons
	
	// ballon zero: reach the finish marker	
	Point ballonZero[7] = 
	{ 
	  Point(0,0),    // 1
	  Point(5,8),    // 2
	  Point(58,8),   // 3
	  Point(58,32),  // 4
	  Point(-15,32), // 5
	  Point(-15,8),  // 6
	  Point(-2,8)    // 7
	};
	ballons[0].SetVertices(ballonZero, 7);
	ballons[0].MoveTo(midX, midY+255);
	ballons[0].color = Color(192,128,0);
	ballons[0].Scale(3.0f, 2.5f);

	// ballon one: avoid fixed structures	
	Point ballonOne[7] = 
	{ 
	  Point(3,0),     // 1
	  Point(-4,-8),   // 2
	  Point(-60,-8),  // 3
	  Point(-60,-30), // 4
	  Point(12,-30),  // 5
	  Point(12,-8),   // 6
	  Point(4,-8)     // 7
	};
	ballons[1].SetVertices(ballonOne, 7);
	ballons[1].MoveTo(midX-200, midY+20);
	ballons[1].color = Color(192,128,0);
	ballons[1].Scale(2.5f, 2.5f);

	// ballon two: do not hit the borders	
	Point ballonTwo[7] = 
	{ 
	  Point(-3,0),    // 1
	  Point(4,8),    // 2
	  Point(80,8),   // 3
	  Point(80,30),  // 4
	  Point(-15,30), // 5
	  Point(-15,8),  // 6
	  Point(-4,8)    // 7
	};
	ballons[2].SetVertices(ballonTwo, 7);
	ballons[2].MoveTo(100, 5);
	ballons[2].color = Color(192,128,0);
	ballons[2].Scale(2.3f, 2.5f);

	// ballon three: move to START	
	Point ballonThree[4] = 
	{ 
	  Point(-37,-11), // 1
	  Point(37,-11),  // 2
	  Point(37,11),   // 3
	  Point(-37,11)   // 4
	};
	ballons[3].SetVertices(ballonThree, 4);
	ballons[3].MoveTo(midX, midY - 250);
	ballons[3].color = Color(0,255,0);
	ballons[3].Scale(1.8f, 2.5f);

	// ----------------------------
	// Level 1 Initialization
	// ----------------------------

	// reset ship to default location, speed and direction
	Point shipVertex[4] = {Point(0,-10), Point(7,10), Point(0,7), Point(-7,10)};
	ship.Reset();
	ship.SetVertices(shipVertex, 4);
	ship.MoveTo(midX, midY);
	ship.color = Color(255,255,0);

	// register ship to scene manager
	scene.RegisterMoving(ship);

	// create fixed object
	Point vetUfig[6] = 
	{ 
	  Point(midX-50,midY-80),  // 1
	  Point(midX-50,midY+40),  // 2
	  Point(midX+50,midY+40),  // 3
	  Point(midX+50,midY-80),  // 4
	  Point(midX+250,midY+90), // 5
	  Point(midX-250,midY+90)  // 6
	};
	Poly * ufig = new Poly(vetUfig,6);
	ufig->color = Color(128,128,128);
	ufig->customType = objFixed;

	// update list of fixed objects
	fixed.push_back(ufig);

	// register fixed object to scene manager
	scene.RegisterStatic(*ufig);

	// create finish marker
	finPosX = midX;
	finPosY = midY+230;
	finish = Rect(-40, -10, 40, 10);
	finish.MoveTo(finPosX,finPosY);
	finish.color = Color(0,255,0);
	finish.customType = objFinish;

	// register finish marker to scene manager
	scene.RegisterStatic(finish);

	// initialize timer
	secs = 10;
	milisecs = 0;
	timeLimit = secs * freq.QuadPart;

	// initial finish color is green
	finishColor = Color(0, 255, 0);
}

/**********************************************************************************/

void Asteroids::Level1Logic()
{
	if (gameState == PLEVEL1)
	{
		// if player press any action key 
		// begin the level and start timer
		if (input.KeyPressed(DIK_UP)    ||
			input.KeyPressed(DIK_DOWN)  ||
			input.KeyPressed(DIK_LEFT)  || 
			input.KeyPressed(DIK_RIGHT))
		{
			prevState = gameState;
			gameState = LEVEL1;
			QueryPerformanceCounter(&start);
		}		

		// do not execute level logic until 
		// state changes from Pre-Level to Level
		return;
	}

	// get elapsed time
	QueryPerformanceCounter(&end);
	LONGLONG remaining = timeLimit - (end.QuadPart - start.QuadPart);
	secs = remaining / freq.QuadPart;
	milisecs = (remaining % freq.QuadPart) * 1000 / freq.QuadPart;

	// if time is up, game over
	if (remaining <= 0)
	{
		// do not show negative number
		secs     = 0;
		milisecs = 0;

		snd.Play(BEEP,0);
		gameState = GAMEOVER;
	}

	// set the finish color
	if (secs < 2)
		// red color
		finishColor = Color(185,0,0);
	else if (secs < 5)
		// yellow color
		finishColor = Color(255,200,0);
	else
		// green color
		finishColor = Color(0,255,0);

	// ---------------------------------------------------

	// Rotate Right
	if (input.KeyPressed(DIK_RIGHT))
		ship.Rotate(4);
	else
	// Rotate Left
	if (input.KeyPressed(DIK_LEFT))
		ship.Rotate(-4);

	// ---------------------------------------------------
	
	// Up Key
	if (input.KeyReleased(DIK_UP))    
		snd.Stop(UP);

	// Down Key
	if (input.KeyReleased(DIK_DOWN))    
		snd.Stop(DOWN);

	// ---------------------------------------------------

	// Translate (move up)
	if (input.KeyPressed(DIK_UP))    
		ship.AccelerateUp();
	if (input.KeyPressRelease(DIK_UP))
		snd.Play(UP,0);
	
	// Translate (move down)
	if (input.KeyPressed(DIK_DOWN))
		ship.AccelerateDown();
	if (input.KeyPressRelease(DIK_DOWN))
		snd.Play(DOWN,0);

	// Space Bar (Fire Missile)
	if (input.KeyPressRelease(DIK_SPACE))
	{
		snd.Play(PULSE,0);
		Missile * mis = new Missile(ship);
		missiles.push_back(mis);
		scene.RegisterMoving(*mis);
	}

	// ---------------------------------------------------

	// move ship by its inertia
	ship.Inertia();

	// move all the missiles
	for (imis = missiles.begin(); imis != missiles.end(); ++imis)
		(*imis)->Move();

	// ---------------------------------------------------

	// test for collision
	if (scene.CollisionDetection())
	{
		// pair of objects in collision
		pair<GameObject*,GameObject*> pairObjs;

		// while the pair returned is not empty, process collision
		while ((pairObjs = scene.OnCollisionObjects()) != pair<GameObject*,GameObject*>(nullptr,nullptr))
		{
			// take first and second object in collision
			Geometry * first  = (Geometry *) pairObjs.first;
			Geometry * second = (Geometry *) pairObjs.second;

			switch (first->customType)
			{
			// -------
			// Missile
			// -------
			case objMissile:
				// destroy missile for every collision 
				// except for finish marker
				if (second->customType != objFinish)
				{
					// missile explosion effect
					Explosion * exp = new Explosion();
					exp->InitParticles(((Missile *) first)->x, 
						               ((Missile *) first)->y,
									   15, 0.1f, 
									   second->color);
					explosions.push_back(exp);

					snd.Play(MISSILEHIT,0);
					// unregister missile from scene manager
					scene.UnRegisterMoving(*first);
					// remove missile from the list
					missiles.remove((Missile*) first);
					// delete missile object
					delete (Missile*) first;				
				}
				break;	
			// -------
			//  ship 
			// -------
			case objShip:
				// get the second object type 
				switch (second->customType)
				{
				
				// Finish Marker
				case objFinish: 
					// player' ship reached finish mark
					if (rocks.empty())
					{
						snd.Play(LEVELUP,0);
						gameState = LEVELCOMPLETE;
						playerTime = (timeLimit / freq.QuadPart) - (secs + 0.001f * milisecs);
					}
					break;

				// Borders or Objects
				case objBorder:
				case objFixed:
					// player' ship collided
					snd.Play(BLOW,0);
					gameState = GAMEOVER;

					// ship explosion
					Explosion * exp = new Explosion();
					exp->InitParticles(ship.x, 
						               ship.y,
									   30, 0.3f, 
									   ship.color);
					explosions.push_back(exp);
					break;
				}
				break;

			} // end switch		
		
		} // end while

	} // end test for collision
}

/**********************************************************************************/

void Asteroids::Level1Draw()
{
	if (gameState == PLEVEL1)
	{
		ballons[0].Draw();
		ballons[1].Draw();
		ballons[2].Draw();
		ballons[3].Draw();

		// Draw Text
		sprintf_s(text, "LEVEL 1");
		sysFont->Draw(0, midY-300, text, Color(0,255,0), GetWindowWidth(), 30, DT_CENTER);

		// Draw Instructions
		sprintf_s(text, "Move the ship to");
		sysFont->Draw(0, midY-270, text, Color(0,255,0), GetWindowWidth(), 30, DT_CENTER);
		sprintf_s(text, "START");
		sysFont->Draw(0, midY-250, text, Color(0,255,0), GetWindowWidth(), 30, DT_CENTER);

		sprintf_s(text, "Reach the finish marker in");
		sysFont->Draw(midX-20, midY+285, text, Color(192,128,0));
		sprintf_s(text, "time to complete the level");
		sysFont->Draw(midX-20, midY+305, text, Color(192,128,0));

		sprintf_s(text, "Avoid fixed structures");
		sysFont->Draw(midX-330, midY-45, text, Color(192,128,0));
		sprintf_s(text, "and space waste");
		sysFont->Draw(midX-330, midY-25, text, Color(192,128,0));

		sprintf_s(text, "Movement area is restricted");
		sysFont->Draw(85, 35, text, Color(192,128,0));
		sprintf_s(text, "Don't hit the borders");
		sysFont->Draw(85, 55, text, Color(192,128,0));

		// Draw Text
		sprintf_s(text, "FINISH");
		sysFont->Draw(0, finPosY-35, text, Color(0,255,0), GetWindowWidth(), 30, DT_CENTER);
	}

	// move stars with random velocity
	eSpace->MoveStars();

	// draw stars in new position
	eSpace->DrawStars();

	// draw screen borders
	border[0].Draw();
	border[1].Draw();
	border[2].Draw();
	border[3].Draw();

	// draw missiles
	for (imis = missiles.begin(); imis != missiles.end(); ++imis)
		(*imis)->Draw();

	// draw fixed objects
	for (ifix = fixed.begin(); ifix != fixed.end(); ++ifix)
	{
		switch((*ifix)->type)
		{
		case ObjRectangle: ((Rect *) (*ifix))->Draw(); break;
		case ObjCircle:	((Circle *) (*ifix))->Draw();  break;
		case ObjPolygon: ((Poly *) (*ifix))->Draw(); break;
		}
		
	}

	// animate all the explosions
	iexp = explosions.begin();
	while (iexp != explosions.end())
	{
		if ((*iexp)->Active())
		{
			(*iexp)->MoveParticles();
			(*iexp)->DrawParticles();
			++iexp;
		}
		else
		{
			// clean explosion
			delete *iexp;
			iexp = explosions.erase(iexp);
		}
	}

	if (gameState != LEVELCOMPLETE)
	{
		// draw finish marker
		finish.color = finishColor;
		finish.Draw();

		// draw player' ship
		if ((gameState != GAMEOVER) || (secs == 0 && milisecs == 0))
			ship.Draw();
	}	
		
	// draw Text
	sprintf_s(text, "Space Race");
	sysFont->Draw(0, 10, text, Color(128,128,128), GetWindowWidth(), 30, DT_CENTER);

	if (gameState != LEVELCOMPLETE)
	{	
		// draw remaining time in seconds and miliseconds
		sprintf_s(text, MAXBUFFER, "00:%02d", secs);
		sprintf_s(text + 5, MAXBUFFER - 5, ".%03d", milisecs);

		sysFont->Draw(0, finPosY-8, text, finishColor, GetWindowWidth(), 30, DT_CENTER);
		
		if (gameState != GAMEOVER)
		{
			sprintf_s(text, "<Arrows> - Move Space ship            <Space> - Fire Missile           <ESC> - Exit Game");
			sysFont->Draw(0, GetWindowHeight()-30, text, Color(255,255,255), GetWindowWidth(), 30, DT_CENTER);
		}
	}
}

/**********************************************************************************/

void Asteroids::Level1CleanUp()
{
	// reset time elapsed
	end.QuadPart = 0;
	start.QuadPart = 0;

	// reset key control states
	input.KeyReset();

	// the game may have ended with objects onscreen (like missiles)
	// remove all remaining objects from lists and delete them

	// clean player' ship
	scene.UnRegisterMoving(ship);

	// clean missiles
	for (imis = missiles.begin(); imis != missiles.end(); ++imis)
	{
		scene.UnRegisterMoving(*((Geometry *) *imis));
		delete *imis;			
	}
	missiles.clear();

	// clean rocks
	for (iroc = rocks.begin(); iroc != rocks.end(); ++iroc)
	{
		scene.UnRegisterMoving(*((Geometry *) *iroc));
		delete *iroc;
	}
	rocks.clear();

	// clean fixed objects
	for (ifix = fixed.begin(); ifix != fixed.end(); ++ifix)
	{
		scene.UnRegisterStatic(*((Geometry *) *ifix));
		delete *ifix;
	}
	fixed.clear();

	// clean explosions
	for (iexp = explosions.begin(); iexp != explosions.end(); ++iexp)
		delete *iexp;
	explosions.clear();
	
	// clean finish marker
	scene.UnRegisterStatic(finish);	      
}

/**********************************************************************************/
